using Engine;
using Engine.Graphics;
using Engine.Input;
using Game;
using GameEntitySystem;
using TemplatesDatabase;
using Color = Engine.Color;

namespace SCIENEW.VoltNet {
    public class SubsystemVoltNet : Subsystem, IUpdateable, IDrawable{
        public UpdateOrder UpdateOrder => UpdateOrder.Default;
        public int[] DrawOrders => [2000];

        PrimitivesRenderer3D m_primitivesRenderer3D = new();
        FlatBatch3D m_flatBath;

        public SubsystemTerrain m_subsystemTerrain;
        public SubsystemTime m_subsystemTime;

        protected bool m_debugCanDraw;
        double m_lastSimulateTime = 0;

        /// <summary>
        /// 每个坐标点对应的用电器或者发电机
        /// </summary>
        public Dictionary<Point3, VoltElement> m_elements = new();

        /// <summary>
        /// 所有连接
        /// </summary>
        public List<VoltConnection> m_connections = new();

        public override void Load(ValuesDictionary valuesDictionary) {
            base.Load(valuesDictionary);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_subsystemTime = Project.FindSubsystem<SubsystemTime>(true);
            m_flatBath = m_primitivesRenderer3D.FlatBatch(0, DepthStencilState.None);
            m_lastSimulateTime = m_subsystemTime.GameTime;
        }

        public void OnBlockGenerated(int value, int x, int y, int z, bool isLoaded) {
            OnBlockAdded(value, 0, x, y, z);
        }

        public void OnBlockAdded(int value, int oldValue, int x, int y, int z) {
            DeviceBlock deviceBlock = DevicesBlockManager.GetDevice(value);
            if (deviceBlock is not IVoltDevice voltDevice) return;

            Point3 position = new(x, y, z);
            VoltElement? voltElement = voltDevice.GetVoltElement(this, m_subsystemTerrain, position, value);
            if (voltElement == null) return;

            if (m_elements.TryGetValue(position, out VoltElement oldElement)) {
                if (oldElement.GetType() != voltElement.GetType()) {// transfer不一致，移除旧的并添加新的
                    m_elements.Remove(position);
                    voltElement.Position = position;
                    m_elements.Add(position, voltElement);
                    voltElement.OnAdded();
                }
            }
            else {
                voltElement.Position = position;
                m_elements.Add(position, voltElement);//添加新的
                voltElement.OnAdded();
            }
            UpdateConnections(position, voltDevice, value);
        }

        public void OnBlockRemoved(int value, int newValue, int x, int y, int z) {
            Point3 position = new(x, y, z);
            if (m_elements.ContainsKey(position)) {
                m_elements[position].OnRemoved();
                m_elements.Remove(position); //删除元素
            }
            UpdateConnections(position, null, -1);
        }

        public void OnBlockModified(int value, int oldValue, int x, int y, int z) {
            OnBlockRemoved(oldValue, value, x, y, z);
            OnBlockAdded(value, oldValue, x, y, z);
        }

        public void UpdateConnections(Point3 position, IVoltDevice? voltDevice, int blockValue) {
            List<VoltConnection> cacheConnections = new(m_connections);
            foreach (var connection in cacheConnections) {//删除存在指定位置的连接
                if (connection.m_containsElements.Keys.Any(x => x.Point == position)) m_connections.Remove(connection);
            }
            cacheConnections.Clear();

            //添加新的接口与连接
            for (int i = 0; i < 6; i++) {
                if (voltDevice == null) {
                    //移除模式，更新周围管道
                    //UpdatePipeGroup(point + CellFace.FaceToPoint3(i));
                    continue;
                }
                //添加模式，更新连接
                HashSet<int> faceMask = voltDevice.GetFaceMask(blockValue);
                if (!faceMask.Contains(i)) continue;
                CellFace thisCellFace = new(position.X, position.Y, position.Z, i);
                Point3 facingPoint = position + CellFace.FaceToPoint3(i);
                CellFace facingCellFace = new(facingPoint.X, facingPoint.Y, facingPoint.Z, CellFace.OppositeFace(i));

                if (m_elements.TryGetValue(facingPoint, out VoltElement facingVoltElement) && m_elements.TryGetValue(position, out VoltElement thisVoltElement)) {
                    Dictionary<CellFace, VoltElement> containsElement = new() { { thisCellFace, thisVoltElement }, { facingCellFace, facingVoltElement } };
                    VoltConnection voltConnection = new(containsElement);
                    m_connections.Add(voltConnection);
                }
            }
        }

        public void Draw(Camera camera, int drawOrder) {
            if (!m_debugCanDraw) return;

            foreach (var element in m_elements) {
                m_flatBath.QueueBoundingBox(new BoundingBox(new Vector3(element.Key), new Vector3(element.Key) + Vector3.One), Color.Green);
            }
            foreach (var connection in m_connections) {
                Vector3 center = connection.CalcCenterPos();
                foreach (var element in connection.m_containsElements) {
                    Vector3 pos = new Vector3(element.Key.Point) + new Vector3(0.5f) + CellFace.FaceToVector3(element.Key.Face) * 0.4f;
                    m_flatBath.QueueLine(pos, center, Color.Yellow, Color.Orange);
                }
            }
            m_primitivesRenderer3D.Flush(camera.ViewProjectionMatrix);
        }

        public void Update(float dt) {
            if (m_subsystemTime.PeriodicGameTimeEvent(1f / 50, 0)) {
                foreach (var connection in m_connections) {
                    connection.Simulate((float)(m_subsystemTime.GameTime - m_lastSimulateTime));
                }
                m_lastSimulateTime = m_subsystemTime.GameTime;
            }

            if (Keyboard.IsKeyDownOnce(Key.F4)) m_debugCanDraw = !m_debugCanDraw;
        }
    }
}